/**
 * @file wlan_11h.c
 *
 *  @brief Implementation of 802.11h wlan_11h module
 *
 *  Driver implementation of the 11h standard.  Contains the following:
 *    - firmware command preparation and response handling
 *    - 11h element identification for elements passed in scan responses
 *
 *  Requires use of the following preprocessor define:
 *    - ENABLE_802_11H
 *
 *  (c) Copyright 2003-2008, Marvell International Ltd.
 *  All Rights Reserved
 */

#include "precomp.h"


/********************************************************
		Local Definitions
********************************************************/

/********************************************************
		Local Variables
********************************************************/
//! U-NII sub-band config : Start Channel = 36, NumChans = 4
static const
IEEEtypes_SupportChan_Subband_t wlan_11h_unii_lower_band = {36, 4};

//! U-NII sub-band config : Start Channel = 52, NumChans = 4
static const
IEEEtypes_SupportChan_Subband_t wlan_11h_unii_middle_band = {52, 4};

//! U-NII sub-band config : Start Channel = 100, NumChans = 11
static const
IEEEtypes_SupportChan_Subband_t wlan_11h_unii_mid_upper_band = {100, 11};

//! U-NII sub-band config : Start Channel = 149, NumChans = 4
static const
IEEEtypes_SupportChan_Subband_t wlan_11h_unii_upper_band = {149, 4};

/********************************************************
		Global Variables
********************************************************/

/********************************************************
		Local Functions
********************************************************/
/**
 *  @brief Return whether the driver is currently setup to use 11h or not.
 *
 *  Association/Join commands are dyanmic in that they enable 11h in the
 *    driver/firmware when they are detected in the existing BSS.
 *
 *  @param Adapter  Private driver information structure
 *
 *  @return
 *    - TRUE if 11h is enabled
 *    - FALSE otherwise
 */
static int wlan_11h_is_enabled(PMRVDRV_ADAPTER Adapter)
{
    wlan_11h_state_t *pState11h = &Adapter->state11h;

    return (pState11h->is11hEnabled ? TRUE : FALSE);
}

/**
 *  @brief Setup the Supported Channel IE sent in association requests
 *
 *  The Supported Channels IE is requird to be sent when the spectrum
 *    management capability is enabled in Assoc_Req frame. The element
 *    contains a starting channel and number of channels tuple for each
 *    subband the STA supports.
 *    This information is based on the operating region.
 *
 *  @param Adapter  Private driver information structure
 *  @param pSupChan Output parameter: Pointer to the Supported Chan element
 *                  setup by thsi function.
 *
 *  @return
 *    - Length of the returned element in pSupChan output parameter
 *    - 0 if returned element is not setup
 */
static
int wlan_11h_set_supp_channels_ie(PMRVDRV_ADAPTER Adapter,
                                  IEEEtypes_SupportedChannels_t* pSupChan)
{
    int numSubbands = 0;
    int retLen = 0;

    NdisZeroMemory(pSupChan, sizeof(IEEEtypes_SupportedChannels_t));

    /*
     * Set the supported channel elements based on the region code,
     *   incrementing numSubbands for each subband we append to the
     *   element.
     */
    switch (Adapter->RegionCode)
    {
    case 0x10:  /* USA FCC  */
    case 0x20:  /* Canada IC    */
        pSupChan->subband[numSubbands++] = wlan_11h_unii_lower_band;
        pSupChan->subband[numSubbands++] = wlan_11h_unii_middle_band;
        pSupChan->subband[numSubbands++] = wlan_11h_unii_mid_upper_band;
        pSupChan->subband[numSubbands++] = wlan_11h_unii_upper_band;
        break;
    case 0x30:  /* Europe ETSI  */
        pSupChan->subband[numSubbands++] = wlan_11h_unii_lower_band;
        pSupChan->subband[numSubbands++] = wlan_11h_unii_middle_band;
        pSupChan->subband[numSubbands++] = wlan_11h_unii_mid_upper_band;
        break;
    default:
        break;
    }

    /*
     * If we have setup any supported subbands in the element, return a
     *    valid IE along with its size, else return 0.
     */
    if (numSubbands)
    {
        pSupChan->elementId = SUPPORTED_CHANNELS;
        pSupChan->len = numSubbands * sizeof(IEEEtypes_SupportChan_Subband_t);

        retLen = (pSupChan->len
                  + sizeof(pSupChan->len) + sizeof(pSupChan->elementId));

        HexDump(DBG_11H, "11h: SupChan", (UCHAR *)pSupChan, retLen);
    }

    return retLen;
}

#ifdef ENABLE_802_11H_TPC
/**
 *  @brief Set the local power constraint in the firmware
 *
 *  Construct and send a CMD_802_11_TPC_INFO command with the local power
 *    constraint.
 *
 *  @param Adapter         Private driver information structure
 *  @param channel         Channel to which the power constraint applies
 *  @param powerConstraint Power constraint to be applied on the channel
 *
 *  @return                NDIS_STATUS_SUCCESS or NDIS_STATUS_FAILURE
 */
static NDIS_STATUS wlan_11h_set_local_power_constraint(PMRVDRV_ADAPTER Adapter,
                                                       UCHAR channel,
                                                       UCHAR powerConstraint)
{
    NDIS_STATUS ret = NDIS_STATUS_SUCCESS;
    wlan_11h_tpc_info_param_t  tpcInfoParam;

    tpcInfoParam.Chan = channel;
    tpcInfoParam.PowerConstraint = powerConstraint;

    DBGPRINT(DBG_11H, (L"11h: Set Local Constraint = %d\r\n",
             tpcInfoParam.PowerConstraint));

    /* This command processing is not implemented yet */
    ret = PrepareAndSendCommand(Adapter,
                                HostCmd_CMD_802_11_TPC_INFO,
                                HostCmd_ACT_GEN_SET,
                                HostCmd_OPTION_USE_INT,
                                0,
			        HostCmd_PENDING_ON_NONE,
			        0,
                                FALSE,
			        NULL,
			        NULL,
			        NULL,
			        &tpcInfoParam);

    if (ret)
    {
        DBGPRINT(DBG_11H, (L"11h: Error: Send TPC_INFO CMD: %d\r\n", ret));
        ret = NDIS_STATUS_FAILURE;
    }

    return ret;
}
#endif

/**
 *  @brief  Utility function to process a join to an infrastructure BSS
 *
 *  @param Adapter     Private driver information structure
 *  @param ppBuffer    Output parameter: Pointer to the TLV output buffer,
 *                     modified on return to point after the appended 11h TLVs
 *  @param channel     Channel on which we are joining the BSS
 *  @param p11hBssInfo Pointer to the 11h BSS information for this network
 *                     that was parsed out of the scan response.
 *
 *  @return            Integer number of bytes appended to the TLV output
 *                     buffer (ppBuffer)
 */
static int wlan_11h_process_infra_join(PMRVDRV_ADAPTER Adapter,
                                       UCHAR **ppBuffer,
                                       UCHAR channel,
                                       wlan_11h_bss_info_t *p11hBssInfo)
{
    MrvlIEtypesHeader_t ieHeader;
    IEEEtypes_SupportedChannels_t supChanIe;
    int retLen = 0;
    int supChanLen = 0;

    /* Null Checks */
    if (ppBuffer == 0) return 0;
    if (*ppBuffer == 0) return 0;

#ifdef ENABLE_802_11H_TPC
    /* Set the local constraint configured in the firmware */
    wlan_11h_set_local_power_constraint(Adapter, channel,
                                        (p11hBssInfo->powerConstraint.localConstraint));
#endif

    /* Setup the Supported Channels IE */
    supChanLen = wlan_11h_set_supp_channels_ie(Adapter, &supChanIe);

    /*
     * If we returned a valid Supported Channels IE, wrap and append it
     */
    if (supChanLen)
    {
        /* Wrap the supported channels IE with a passthrough TLV type */
        ieHeader.Type = TLV_TYPE_PASSTHROUGH;
        ieHeader.Len  = supChanLen;
        NdisMoveMemory(*ppBuffer, &ieHeader, sizeof(ieHeader));

        /* Increment the return size and the return buffer pointer param */
        *ppBuffer += sizeof(ieHeader);
        retLen    += sizeof(ieHeader);

        /* Copy the supported channels IE to the output buf, advance pointer */
        NdisMoveMemory(*ppBuffer, &supChanIe, supChanLen);
        *ppBuffer += supChanLen;
        retLen    += supChanLen;
    }

    return retLen;
}

/**
 *  @brief Convert an IEEE formatted IE to 16-bit ID/Len Marvell
 *         proprietary format
 *
 *  @param pOutBuf Output parameter: Buffer to output Marvell formatted IE
 *  @param pInIe   Pointer to IEEE IE to be converted to Marvell format
 *
 *  @return        Number of bytes output to pOutBuf parameter return
 */
static int wlan_11h_convert_ieee_to_mrvl_ie(char* pOutBuf,
                                            const char* pInIe)
{
    MrvlIEtypesHeader_t mrvlIeHdr;
    char* pTmpBuf = pOutBuf;

    /* Assign the Element Id and Len to the Marvell struct attributes */
    mrvlIeHdr.Type = pInIe[0];
    mrvlIeHdr.Len  = pInIe[1];

    /* If the element ID is zero, return without doing any copying */
    if (mrvlIeHdr.Type == 0)
    {
        return 0;
    }

    /* Copy the header to the buffer pointer */
    NdisMoveMemory(pTmpBuf, &mrvlIeHdr, sizeof(mrvlIeHdr));

    /* Increment the temp buffer pointer by the size appended */
    pTmpBuf += sizeof(mrvlIeHdr);

    /* Append the data section of the IE; length given by the IEEE IE length */
    NdisMoveMemory(pTmpBuf, pInIe + 2, pInIe[1]);

    /* Return the number of bytes appended to pBuf */
    return (sizeof(mrvlIeHdr) + pInIe[1]);
}

#ifdef ENABLE_802_11H_ADHOC
/**
 *  @brief Setup the IBSS DFS element passed to the firmware in adhoc start
 *         and join commands
 *
 *  The DFS Owner and recovery fields are set to be our MAC address and
 *    a predetermined constant recovery value.  If we are joining an adhoc
 *    network, these values are replaced with the existing IBSS values.
 *    They are valid only when starting a new IBSS.
 *
 *  The IBSS DFS Element is variable in size based on the number of
 *    channels supported in our current region.
 *
 *  @param Adapter  Private driver information structure
 *  @param pDfs     Output parameter: Pointer to the IBSS DFS element setup by
 *                  this function.
 *
 *  @return
 *    - Length of the returned element in pDfs output parameter
 *    - 0 if returned element is not setup
 */
static int wlan_11h_set_ibss_dfs_ie(PMRVDRV_ADAPTER Adapter,
                                    IEEEtypes_IBSS_DFS_t* pDfs)
{
    int numChans = 0;
    MeasRptBasicMap_t initialMap;
    wlan_adapter* Adapter = priv->adapter;

    PRINTM(INFO, "11h: IBSS DFS Element, 11D parsed region: 0x%x\n",
           Adapter->cur_region_channel.region);

    NdisZeroMemory(pDfs, sizeof(IEEEtypes_IBSS_DFS_t));

    /*
     * A basic measurement report is included with each channel in the
     *   map field.  Initial value for the map for each supported channel
     *   is with only the unmeasured bit set.
     */
    NdisZeroMemory(&initialMap, sizeof(initialMap));
    initialMap.unmeasured = 1;

    /* Set the DFS Owner and recovery interval fields */
    NdisMoveMemory(pDfs->dfsOwner, Adapter->CurrentAddr, sizeof(pDfs->dfsOwner));
    pDfs->dfsRecoveryInterval = WLAN_11H_DEFAULT_DFS_RECOVERY_INTERVAL;

    for (; (numChans < Adapter->parsed_region_chan.NoOfChan)
             && (numChans < WLAN_11H_MAX_IBSS_DFS_CHANNELS); numChans++) {
        pDfs->channelMap[numChans].channelNumber =
            Adapter->parsed_region_chan.chanPwr[numChans].chan;

        /*
         * Set the inital map field with a basic measurement
         */
        pDfs->channelMap[numChans].rptMap = initialMap;
    }

    /*
     * If we have an established channel map, include it and return
     *   a valid DFS element
     */
    if (numChans) {
        DBGPRINT(DBG_11H, "11h: Added %d channels to IBSS DFS Map\r\n", numChans);

        pDfs->elementId = IBSS_DFS;
        pDfs->len = (sizeof(pDfs->dfsOwner) + sizeof(pDfs->dfsRecoveryInterval)
                     + numChans * sizeof(IEEEtypes_ChannelMap_t));

        return (pDfs->len + sizeof(pDfs->len) + sizeof(pDfs->elementId));
    }

    /* Ensure the element is zeroed out for an invalid return */
    NdisZeroMemory(pDfs, sizeof(IEEEtypes_IBSS_DFS_t));

    return 0;
}

/**
 *  @brief Utility function to process a start or join to an adhoc network
 *
 *  Add the elements to the TLV buffer needed in the start/join adhoc commands:
 *       - IBSS DFS IE
 *       - Quiet IE
 *
 *  Also send the local constraint to the firmware in a TPC_INFO command.
 *
 *  @param Adapter     Private driver information structure
 *  @param ppBuffer    Output parameter: Pointer to the TLV output buffer,
 *                     modified on return to point after the appended 11h TLVs
 *  @param channel     Channel on which we are starting/joining the IBSS
 *  @param p11hBssInfo Pointer to the 11h BSS information for this network
 *                     that was parsed out of the scan response.  NULL
 *                     indicates we are starting the adhoc network
 *
 *  @return            Integer number of bytes appended to the TLV output
 *                     buffer (ppBuffer)
 */
static int wlan_11h_process_adhoc(PMRVDRV_ADAPTER Adapter,
                                  UCHAR **ppBuffer,
                                  USHORT channel,
                                  wlan_11h_bss_info_t *p11hBssInfo )
{
    IEEEtypes_IBSS_DFS_t dfsElem;
    int sizeAppended;
    int retLen = 0;
    s8  localConstraint=0;

    /* Format our own IBSS DFS Element.  Include our channel map fields */
    wlan_11h_set_ibss_dfs_ie(Adapter, &dfsElem);

    if (p11hBssInfo) {
        /*
         * Copy the DFS Owner/Recovery Interval from the BSS we are joining
         */
        NdisMoveMemory(dfsElem.dfsOwner,
                       p11hBssInfo->ibssDfs.dfsOwner,
                       sizeof(dfsElem.dfsOwner));
        dfsElem.dfsRecoveryInterval = p11hBssInfo->ibssDfs.dfsRecoveryInterval;
    }

    /* Append the dfs element to the TLV buffer */
    sizeAppended = wlan_11h_convert_ieee_to_mrvl_ie(*ppBuffer,
                                                    (u8*)&dfsElem);

    HexDump(DBG_11H, "11h: IBSS-DFS", (void*)*ppBuffer, sizeAppended);
    *ppBuffer += sizeAppended;
    retLen    += sizeAppended;

    /*
     * Check to see if we are joining a network.  Join is indicated by the
     *   BSS Info pointer being valid (not NULL)
     */
    if (p11hBssInfo)
    {
        /*
         * If there was a quiet element, include it in adhoc join command
         */
        if (p11hBssInfo->quiet.elementId == QUIET)
        {
            sizeAppended
                = wlan_11h_convert_ieee_to_mrvl_ie(*ppBuffer,
                                                   (u8*)&p11hBssInfo->quiet);
            HexDump(DBG_11H, "11h: Quiet", (void*)*ppBuffer, sizeAppended);
            *ppBuffer += sizeAppended;
            retLen    += sizeAppended;
        }

        /* Copy the local constraint from the network */
        localConstraint = p11hBssInfo->powerConstraint.localConstraint;
    } else {
        /*
         * If we are the adhoc starter, we can add a quiet element
         */
       if(Adapter->state11h.quiet_ie.quietPeriod) {
   	      sizeAppended = wlan_11h_convert_ieee_to_mrvl_ie(*ppBuffer,
   	                                                      (u8*)&Adapter->state11h.quiet_ie);
   	      HexDump(DBG_11H, "11h: Quiet", (void*)*ppBuffer, sizeAppended);
   	      *ppBuffer += sizeAppended;
   	      retLen    += sizeAppended;
   
   	      /* Use the localConstraint configured in the driver state */
   	      localConstraint = Adapter->state11h.usrDefPowerConstraint;
        }    
    }

#ifdef ENABLE_802_11H_TPC
    /* Set the local constraint configured in the firmware */
    wlan_11h_set_local_power_constraint(priv, channel, localConstraint);
#endif

    return retLen;
}
#endif


/********************************************************
		Global Functions
********************************************************/
/**
 *
 *  @brief Initialize the 11h parameters
 *
 *  @param Adapter  Private driver information structure
 *
 *  @return      void
 */
void wlan_11h_init(PMRVDRV_ADAPTER Adapter)
{
    wlan_11h_state_t *pState11h = &Adapter->state11h;
    IEEEtypes_Quiet_t *pQuiet = &Adapter->state11h.quiet_ie;


    pState11h->usrDefPowerConstraint= WLAN_11H_TPC_POWERCONSTRAINT;
    pState11h->minTxPowerCapability = WLAN_11H_TPC_POWERCAPABILITY_MIN;
    pState11h->maxTxPowerCapability = WLAN_11H_TPC_POWERCAPABILITY_MAX;

    /*
     * By default, the driver should have its preference set as 11h being
     *    activated when starting an ad hoc network.  For infrastructre
     *    and ad hoc join, 11h will be sensed and activated accordingly.
     */
    pState11h->is11hEnabled = TRUE;

    /*
     * On power up, the firmware should have 11h support inactive.
     */
    pState11h->is11hActive = FALSE;

    /*Initialize quiet_ie*/
    NdisZeroMemory(pQuiet, sizeof(IEEEtypes_Quiet_t));
    
    pQuiet->elementId = QUIET;
    pQuiet->len = (sizeof(pQuiet->quietCount) + sizeof(pQuiet->quietPeriod)
                   + sizeof(pQuiet->quietDuration)
                   + sizeof(pQuiet->quietOffset));
}


/**
 *  @brief Query 11h driver tx enable/disable state
 *
 *  @param Adapter  Private driver information structure
 *
 *  @return
 *    - TRUE if 11h has disabled the transmit interface
 *    - FALSE otherwise
 */
int wlan_11h_is_tx_disabled(PMRVDRV_ADAPTER Adapter)
{
    wlan_11h_state_t* pState11h = &Adapter->state11h;

    return (pState11h->txDisabled);
}


/**
 *  @brief Disable the transmit interface and record the state.
 *
 *  @param Adapter  Private driver information structure
 *
 *  @return      void
 */
void wlan_11h_tx_disable(PMRVDRV_ADAPTER Adapter)
{
    wlan_11h_state_t* pState11h = &Adapter->state11h;

    pState11h->txDisabled = TRUE;
}


/**
 *  @brief Enable the transmit interface and record the state.
 *
 *  @param Adapter  Private driver information structure
 *
 *  @return      void
 */
void wlan_11h_tx_enable(PMRVDRV_ADAPTER Adapter)
{
    wlan_11h_state_t* pState11h = &Adapter->state11h;

    pState11h->txDisabled = FALSE;
}


/**
 *  @brief Enable or Disable the 11h extensions in the firmware
 *
 *  @param Adapter  Private driver information structure
 *  @param flag     Enable 11h if TRUE, disable otherwise
 *
 *  @return         NDIS_STATUS_SUCCESS or NDIS_STATUS_FAILURE
 */
NDIS_STATUS wlan_11h_activate(PMRVDRV_ADAPTER Adapter,
                              int flag)
{
    wlan_11h_state_t* pState11h = &Adapter->state11h;
    int enable = flag ? 1 : 0;
    NDIS_STATUS ret = NDIS_STATUS_SUCCESS;

    /*
     * Send cmd to FW to enable/disable 11h function in firmware
     */
    ret = PrepareAndSendCommand(Adapter,
                                HostCmd_CMD_802_11_SNMP_MIB,
                                HostCmd_ACT_GEN_SET,
                                HostCmd_OPTION_USE_INT,
                                OID_MRVL_802_11H_ENABLE,
			        HostCmd_PENDING_ON_NONE,
			        0,
                                FALSE,
			        NULL,
			        NULL,
			        NULL,
			        &enable);

    if (ret)
    {
        ret = NDIS_STATUS_FAILURE;
    }
    else
    {
        /* Set flag in driver 11h state */
        pState11h->is11hActive = flag;
    }
    
    DBGPRINT(DBG_11H, (L"11h: %S\r\n", enable ? "Activate" : "Deactivate"));

    return ret;
}


/**
 *  @brief Query 11h firmware enabled state.
 *
 *  Return whether the firmware currently has 11h extensions enabled
 *
 *  @param Adapter  Private driver information structure
 *
 *  @return
 *    - TRUE if 11h has been activated in the firmware
 *    - FALSE otherwise
 */
int wlan_11h_is_active(PMRVDRV_ADAPTER Adapter)
{
    wlan_11h_state_t *pState11h = &Adapter->state11h;

    return (pState11h->is11hActive ? TRUE : FALSE);
}


/**
 *  @brief Process an element from a scan response, copy relevant info for 11h
 *
 *  @param p11hBssInfo  Output parameter: Pointer to the 11h BSS information
 *                      for the network that is being processed
 *  @param pElement     Pointer to the current IE we are inspecting for 11h
 *                      relevance
 *
 *  @return             NDIS_STATUS_SUCCESS or NDIS_STATUS_FAILURE
 */
NDIS_STATUS
wlan_11h_process_bss_elem(wlan_11h_bss_info_t *p11hBssInfo,
                          const UCHAR *pElement)
{
    NDIS_STATUS ret = NDIS_STATUS_SUCCESS;

    switch (*pElement)
    {
        case POWER_CONSTRAINT:
            DBGPRINT(DBG_11H, (L"11h: Power Constraint IE Found\r\n"));
            p11hBssInfo->sensed11h = 1;
            NdisMoveMemory(&p11hBssInfo->powerConstraint,
                           pElement,
                           sizeof(IEEEtypes_PowerConstraint_t));
            break;

        case POWER_CAPABILITY:
            DBGPRINT(DBG_11H, (L"11h: Power Capability IE Found\r\n"));
            p11hBssInfo->sensed11h = 1;
            NdisMoveMemory(&p11hBssInfo->powerCapability,
                           pElement,
                           sizeof(IEEEtypes_PowerCapability_t));
            break;

        case TPC_REPORT:
            DBGPRINT(DBG_11H, (L"11h: Tpc Report IE Found\r\n"));
            p11hBssInfo->sensed11h = 1;
            NdisMoveMemory(&p11hBssInfo->tpcReport,
                           pElement,
                           sizeof(IEEEtypes_TPCReport_t));
            break;

        case CHANNEL_SWITCH_ANN:
            DBGPRINT(DBG_11H, (L"11h: Channel Switch Announce IE Found\r\n"));
            p11hBssInfo->sensed11h = 1;
            break;

        case QUIET:
            DBGPRINT(DBG_11H, (L"11h: Quiet IE Found\r\n"));
            p11hBssInfo->sensed11h = 1;
            NdisMoveMemory(&p11hBssInfo->quiet,
                           pElement,
                           sizeof(IEEEtypes_Quiet_t));
            break;

        case IBSS_DFS:
            DBGPRINT(DBG_11H, (L"11h: Ibss Dfs IE Found\r\n"));
            p11hBssInfo->sensed11h = 1;
            NdisMoveMemory(&p11hBssInfo->ibssDfs,
                           pElement,
                           sizeof(IEEEtypes_IBSS_DFS_t));
            break;

        case SUPPORTED_CHANNELS:
        case TPC_REQUEST:
            /*
             * These elements are not in beacons/probe responses.  Included here
             *   to cover set of enumerated 11h elements.
             */
            break;

        default:
            ret = NDIS_STATUS_FAILURE;
    }

    return ret;
}


/**
 *  @brief Process an TLV buffer for a pending BSS Join command for
 *         both adhoc and infra networks
 *
 *  The TLV command processing for a BSS join for either adhoc or
 *    infrastructure network is performed with this function.  The
 *    capability bits are inspected for the IBSS flag and the appropriate
 *    local routines are called to setup the necessary TLVs.
 *
 *  Activate 11h functionality in the firmware if the spectrum management
 *    capability bit is found in the network infromation for the BSS we are
 *    joining.
 *
 *  @param Adapter     Private driver information structure
 *  @param ppBuffer    Output parameter: Pointer to the TLV output buffer, 
 *                     modified on return to point after the appended 11h TLVs
 *  @param pCapInfo    Pointer to the capability info for the BSS to join
 *
 *  @return            Integer number of bytes appended to the TLV output
 *                     buffer (ppBuffer), NDIS_STATUS_FAILURE, 
 *                     or NDIS_STATUS_SUCCESS
 */
int wlan_11h_process_join(PMRVDRV_ADAPTER Adapter,
                          UCHAR **ppBuffer,
                          IEEEtypes_CapInfo_t *pCapInfo)
{
    PBSS_DESCRIPTION_SET_ALL_FIELDS pBssDescList;
    wlan_11h_bss_info_t *p11hBssInfo;
    ULONG  ulAttemptSSIDIdx;
    UCHAR  channel;
    int ret = 0;

    ulAttemptSSIDIdx = Adapter->ulAttemptedBSSIDIndex;
    pBssDescList = Adapter->PSBssDescList;
    p11hBssInfo = (wlan_11h_bss_info_t *)&pBssDescList[ulAttemptSSIDIdx].wlan_11h_bss_info;
    channel = (UCHAR)pBssDescList[ulAttemptSSIDIdx].PhyParamSet.DsParamSet.CurrentChan;

    if (p11hBssInfo->sensed11h)
    {
        /* No use having 11h enabled without 11d enabled */
        if( !Adapter->Enable80211D )
        { 
        Enable_11d(Adapter, ENABLE_11D);
        SetDomainInfo(Adapter, &(pBssDescList[ulAttemptSSIDIdx].CountryInfo));
        }   

        /* Activate 11h functions in firmware, turns on capability bit */
        wlan_11h_activate(Adapter, TRUE);
        pCapInfo->SpectrumMgmt = 1;

        if (pCapInfo->Ibss)
        {
            DBGPRINT(DBG_11H, (L"11h: Adhoc join: Sensed\r\n"));
#ifdef ENABLE_802_11H_ADHOC
            ret = wlan_11h_process_adhoc(Adapter, ppBuffer, channel,
                                         p11hBssInfo);
#endif
        }
        else
        {
            DBGPRINT(DBG_11H, (L"11h: Infra join: Sensed\r\n"));
            ret = wlan_11h_process_infra_join(Adapter, ppBuffer,
                                              channel, p11hBssInfo);
        }
    }
    else
    {
        /* Deactivate 11h functions in the firmware */
        wlan_11h_activate(Adapter, FALSE);
        pCapInfo->SpectrumMgmt = 0;
    }

    return ret;
}
